/**
 * XQuery to Scala Translator
 * Fatemeh Borran-Dejnabadi
 * 26 December 2004
 */
// $Id: Translator.scala 10847 2007-04-26 14:16:28Z michelou $

package xquery2src

/** This class implements the translator of xquery to scala code.
 */
class Translator extends Analyzer {

  /** Indentation string.
   */
  override val step:String = "   "

  /** indentation level.
   */
  //override var level: int = 0
  level = 0

  /** flag for variables in for, let, some and every
   */
  //override var flag:boolean = true; // for, some, every = true
				    // let = false;
  flag = true

  /** flag for predicates
   */
  var predflag:boolean = false;

  /** flag for for-yield expressions (xml expressions)
   */
  var yieldflag:boolean = false;

  /** Print an end-of-line character and indent the following
   *  line appropriately.
   */
  def appendLine(tree: Tree): Translator = {
    tree.translation.append("\n")
    var i = 0
    while (i < level) {
      tree.translation.append(step);
      i += 1
    }
    this
  }

  /** Translate the tree.
   */
  def translate(tree:Tree):StringBuffer = tree match {
    case Bad(pos) =>
      tree.translation.append("\nBad!!!\n");
      return tree.translation;

    case XMLExpr(pos, tag, attrExpr, content) =>
      tree.translation.append("<");
      tree.translation.append(tag);
      if (attrExpr.length != 0) {
	tree.translation.append(" ");
	tree.translation.append(translate(attrExpr));
      }
      if (content != null) {
	tree.translation.append(">");
	yieldflag = true;
	tree.translation.append(translate(content));
	tree.translation.append("</");
	tree.translation.append(tag);
	tree.translation.append(">;");
      }
      else
	tree.translation.append("/>;");
      //yieldflag = false;
      return tree.translation;

    case AttrExpr(pos, att, value) =>
      tree.translation.append(att);
      tree.translation.append(" = ");
      tree.translation.append(translate(value));
      return tree.translation;

    case Sequence(pos, singleExprs) =>
      if (yieldflag) {
	var i:int = 0;
	while (i < singleExprs.length) {
          if (i > 0) tree.translation.append("} {");
          tree.translation.append(translate(singleExprs.apply(i)));
	  i = i + 1;
	}
	//yieldflag = false;
      }
      else {
	var write:boolean = false;
	var i:int = 0;
	while (i < singleExprs.length) {
          if (i == 0) {
	    singleExprs.apply(i) match {
	      case Flwor(p, t, w, o, r)  =>
		write = true;
		tree.translation.append("Nil;");
		indent();
		appendLine(tree);
		tree.translation.append("var res: List[Node] = List();");
		appendLine(tree);
		tree.translation.append(translate(singleExprs.apply(i)));
	      case _ =>
		tree.translation.append(translate(singleExprs.apply(i)));
	    }
          }
          else {
            tree.translation.append(translate(singleExprs.apply(i)));
          }
	  i = i + 1;
	}
	if (write) {
          tree.translation.append("Console.println(res.reverse)");
          write = false;
	}
      }
      return tree.translation;

    case Flwor(pos, types, whereExpr, orderByExprs, returnExpr) =>
      if (yieldflag) { // xml-expr
	indent();
	appendLine(tree);
	tree.translation.append(translate(types));
	if (types.length == 1 && flag == false) { // let-return clause
          tree.translation.append("for (val x <- List(1); ");
	}
	if (whereExpr != null) {
          tree.translation.append(translate(whereExpr));
	}
	else {
          tree.translation.append("true");
	}
	tree.translation.append(") yield {");
	indent();
	appendLine(tree);
	tree.translation.append(translate(returnExpr));
	undent();
	appendLine(tree);
	tree.translation.append("}");
	appendLine(tree);
	//yieldflag = false;
      }
      else {
	tree.translation.append(translate(types));
	if (types.length == 1 && flag == false) { // let-return clause
          tree.translation.append("for (val x <- List(1); ");
	}
	if (whereExpr != null) {
          tree.translation.append(translate(whereExpr));
	}
	else {
          tree.translation.append("true");
	}
	tree.translation.append(") {");
	indent();
	appendLine(tree);
	returnExpr.treeType match {
	  case Types.COLLECTION_TYPE =>
            returnExpr match {
	      case Flwor(p, t, w, o, r) =>
		tree.translation.append(translate(returnExpr));
              case _ =>
		tree.translation.append("(");
		tree.translation.append(translate(returnExpr));
		tree.translation.append(").foreach(n => res = n::res);");
            }
	  case _ =>
            tree.translation.append("res = ");
            tree.translation.append(translate(returnExpr));
            tree.translation.append("::res;");
	}
	undent();
	appendLine(tree);
	tree.translation.append("}");
	appendLine(tree);
	if (orderByExprs != null) {
          tree.translation.append("res = orderBy(res, ");
          tree.translation.append(translate(orderByExprs));
          tree.translation.append(");");
	  appendLine(tree);
	}
      }
      return tree.translation;

    case For(pos, vars) =>
      flag = true;
      tree.translation.append("for (");
      tree.translation.append(translate(vars));
      return tree.translation;

    case Let(pos, vars) =>
      flag = false;
      tree.translation.append(translate(vars));
      appendLine(tree);
      return tree.translation;

    case Some(pos, vars, satisfies) =>
      flag = true;
      tree.translation.append(translate(vars));
      tree.translation.append(translate(satisfies));
      return tree.translation;

    case Every(pos, vars, satisfies) =>
      flag = true;
      tree.translation.append(translate(vars));
      tree.translation.append(translate(satisfies));
      return tree.translation;

    case Var(pos, ident, domain) =>
      flag match {
	case true => // for, some, every
          tree.translation.append("val ");
          tree.translation.append(ident);
          tree.translation.append(" <- ");
          tree.translation.append(translate(domain));
          tree.translation.append("; ");       
	case false => // let
          tree.translation.append("val ");
          tree.translation.append(ident);
          tree.translation.append(" = ");
          tree.translation.append(translate(domain));
          tree.translation.append("; ");                
	case _ =>
          throw new Error("unknown instruction!");
      }
      return tree.translation;
	
    case If(pos, condition, thenIf, elseIf) =>
      indent();
      appendLine(tree);
      tree.translation.append("if (");
      tree.translation.append(translate(condition));
      tree.translation.append(")");
      indent();
      appendLine(tree);
      tree.translation.append(translate(thenIf));
      tree.translation.append(";");
      undent();
      appendLine(tree);
      tree.translation.append("else");
      indent();
      appendLine(tree);
      tree.translation.append(translate(elseIf));
      tree.translation.append(";");
      undent();
      appendLine(tree);
      return tree.translation;

    case BinOp(pos, operation, operand1, operand2) =>
      operation match {
	case AND =>
          tree.translation.append(translate(operand1));
          tree.translation.append(" && ");
          tree.translation.append(translate(operand2));          
	case OR =>
          tree.translation.append(translate(operand1));
          tree.translation.append(" || ");
          tree.translation.append(translate(operand2));       
	case PLUS =>
          tree.translation.append(translate(operand1));
          tree.translation.append(" + ");
          tree.translation.append(translate(operand2));      
	case MINUS =>
          tree.translation.append(translate(operand1));
          tree.translation.append(" - ");
          tree.translation.append(translate(operand2));          
	case TIMES =>
          tree.translation.append(translate(operand1));
          tree.translation.append(" * ");
          tree.translation.append(translate(operand2));
	case DIV =>
          tree.translation.append(translate(operand1));
          tree.translation.append(" / ");
          tree.translation.append(translate(operand2));        
	case MOD =>
          tree.translation.append(translate(operand1));
          tree.translation.append(" % ");
          tree.translation.append(translate(operand2));        
	case EQUAL =>
          if (predflag) {
            tree.translation.append("\"equal\"");
            tree.translation.append(", ");
            tree.translation.append(translate(operand1));
            tree.translation.append(", ");
            tree.translation.append(translate(operand2));
          }
          else {
            tree.translation.append("equal(");
            tree.translation.append(translate(operand1));
            tree.translation.append(", ");
            tree.translation.append(translate(operand2));
            tree.translation.append(")");
          }
	case NOTEQUAL =>
          if (predflag) {
            tree.translation.append("\"not_equal\"");
            tree.translation.append(", ");
            tree.translation.append(translate(operand1));
            tree.translation.append(", ");
            tree.translation.append(translate(operand2));
          }
          else {
            tree.translation.append("not_equal(");
            tree.translation.append(translate(operand1));
            tree.translation.append(", ");
            tree.translation.append(translate(operand2));
            tree.translation.append(")");
          }
	case LTHAN =>
          if (predflag) {
            tree.translation.append("\"less_than\"");
            tree.translation.append(", ");
            tree.translation.append(translate(operand1));
            tree.translation.append(", ");
            tree.translation.append(translate(operand2));
          }
          else {
            tree.translation.append("less_than(");
            tree.translation.append(translate(operand1));
            tree.translation.append(", ");
            tree.translation.append(translate(operand2));
            tree.translation.append(")");
          }
	case LETHAN =>
          if (predflag) {
            tree.translation.append("\"less_equal\"");
            tree.translation.append(", ");
            tree.translation.append(translate(operand1));
            tree.translation.append(", ");
            tree.translation.append(translate(operand2));
          }
          else {
            tree.translation.append("less_equal(");
            tree.translation.append(translate(operand1));
            tree.translation.append(", ");
            tree.translation.append(translate(operand2));
            tree.translation.append(")");
          }
	case GTHAN =>
          if (predflag) {
            tree.translation.append("\"greater_than\"");
            tree.translation.append(", ");
            tree.translation.append(translate(operand1));
            tree.translation.append(", ");
            tree.translation.append(translate(operand2));
          }
          else {
            tree.translation.append("greater_than(");
            tree.translation.append(translate(operand1));
            tree.translation.append(", ");
            tree.translation.append(translate(operand2));
            tree.translation.append(")");
          }
	case GETHAN =>
          if (predflag) {
            tree.translation.append("\"greater_equal\"");
            tree.translation.append(", ");
            tree.translation.append(translate(operand1));
            tree.translation.append(", ");
            tree.translation.append(translate(operand2));
          }
          else {
            tree.translation.append("greater_equal(");
            tree.translation.append(translate(operand1));
            tree.translation.append(", ");
            tree.translation.append(translate(operand2));
            tree.translation.append(")");
          }
	case SLASH =>
	  operand1.treeType match {
	    case FunType(params, resType) => // operand1 is a function
	      operand2.treeType match {
		case Types.ELEMENT_TYPE => // operand2 is a root element
		  tree.translation.append(translate(operand1)); // operand2 should not be translate in scala!!!
		case _ => // operand2 is not a root element
		  tree.translation.append(translate(operand1));
		  tree.translation.append(" \\ ");
		  tree.translation.append(translate(operand2));
	      }
	    case _ => // operand1 is not a function
	      operand2.treeType match {
		case FunType(params, resType) => // operand2 is a function
		  tree.translation.append(translate(operand2));
		  tree.translation.append("(");
		  tree.translation.append(translate(operand1));
		  tree.translation.append(")");
		case Types.ATTRIBUTE_TYPE => // operand2 is an attribute
		  tree.translation.append("attribute(");
		  tree.translation.append(translate(operand1));
		  tree.translation.append(", ");
		  tree.translation.append(translate(operand2));
		  tree.translation.append(")");
		case _ => // operand2 is not a function nor an attribute
		  operand2 match {
		    case Filter(pos, primaryExpr, predicates) => // operand2 is a Filter Tree
		      if (predicates.length != 0) {
			predflag = true;
			tree.translation.append("predicate(");
			tree.translation.append(translate(operand1));
			tree.translation.append(", ");
			tree.translation.append(translate(primaryExpr));
			tree.translation.append(", ");
			tree.translation.append(translate(predicates));
			tree.translation.append(")");
			predflag = false;
		      }
		      else {
			tree.translation.append(translate(operand1));
			tree.translation.append(" \\ ");
			tree.translation.append(translate(operand2));
		      }
		    case _ =>
		      tree.translation.append(translate(operand1));
		      tree.translation.append(" \\ ");
		      tree.translation.append(translate(operand2));
		  }
	      }

	  }	  
	case DSLASH =>
	  operand2.treeType match {
	    case Types.ATTRIBUTE_TYPE => // operand2 is an attribute
	      tree.translation.append("attribute(");
              tree.translation.append(translate(operand1));
              tree.translation.append(", ");
              tree.translation.append(translate(operand2));
              tree.translation.append(")"); 
	    case _ =>
	      operand2 match {
		case Filter(pos, primaryExpr, predicates) => // operand2 is a Filter Tree
		  if (predicates.length != 0) {
		    tree.translation.append("predicate(");
		    tree.translation.append(translate(operand1));
		    tree.translation.append(", ");
		    tree.translation.append(translate(primaryExpr));
		    tree.translation.append(", ");
		    tree.translation.append(translate(predicates));
		    tree.translation.append(")");
		  }
		  else {
		    tree.translation.append(translate(operand1));
		    tree.translation.append(" \\\\ ");
		    tree.translation.append(translate(operand2));
		  }
		case _ =>
		  tree.translation.append(translate(operand1));
		  tree.translation.append(" \\\\ ");
		  tree.translation.append(translate(operand2));
	      }
	  }
	case _ =>
          throw new Error("unknown binary operation!");
      }
      return tree.translation;

    case UniOp(pos, operation, operand) =>
      operation match {
	case PLUS =>
          tree.translation.append(" + ");        
	case MINUS =>
          tree.translation.append(" - ");        
	case SLASH =>
          tree.translation.append(" \\ ");        
	case DSLASH =>
          tree.translation.append(" \\\\ ");       
	case _ =>
          throw new Error("unknown unary operation!");
      }
      tree.translation.append(translate(operand));
      return tree.translation;

    case OrderBy(pos, orderBy, order) =>
      //tree.translation.append(translate(tree.orderBy));
      //tree.translation.append(", ");
      if (order != null) {
	tree.translation.append("\"");
	tree.translation.append(order);
	tree.translation.append("\"");
      }
      else {
	tree.translation.append("\"descending\"");
      }
      return tree.translation;

    case Filter(pos, primaryExpr, predicates) =>
      if (predicates.length != 0) {
	predflag = true;
	tree.translation.append("predicate(");
	tree.translation.append(translate(primaryExpr));
	tree.translation.append(", ");
	tree.translation.append(translate(predicates));
	tree.translation.append(")");
	predflag = false;
      }
      else {
	tree.translation.append(translate(primaryExpr));
      }
      return tree.translation;

    case IntTree(pos, value) =>
      tree.translation.append(value);
      return tree.translation;

    case StringTree(pos, string) =>
      tree.translation.append("\"");
      tree.translation.append(string);
      tree.translation.append("\"");
      return tree.translation;
    
    case Element(pos, name) =>
      tree.translation.append("\"");
      tree.translation.append(name);
      tree.translation.append("\"");      
      return tree.translation;

    case Attribute(pos, name) =>
      tree.translation.append("\"");
      tree.translation.append(name);
      tree.translation.append("\"");
      return tree.translation;

    case Ident(pos, name) =>
      tree.translation.append(name);
      return tree.translation;

    case Expr(pos, expr) =>
      tree.translation.append(" {");     
      tree.translation.append(translate(expr));
      tree.translation.append("} ");
      return tree.translation;

    case FunCall(pos, fname, args) =>
      var trans:StringBuffer = new StringBuffer();
      fname match {
	case DOC =>
          trans.append("doc(");
	case DOCUMENT =>
          trans.append("document(");
	case MIN =>
          trans.append("min(");
	case MAX =>
          trans.append("max(");
	case COUNT =>
          trans.append("count(");
	case SUM =>
          trans.append("sum(");
	case AVG =>
          trans.append("avg(");
	case NOT =>
          trans.append("not(");
	case EMPTY =>
          trans.append("empty(");
	case EXISTS =>
          trans.append("exists(");
	case POSITION =>
          trans.append("\"position\"");
          tree.translation = trans;
	  return tree.translation;
	case CONTAINS =>
          trans.append("contains(");
	case DISTINCT =>
          trans.append("distinct_values(");
	case STARTSWITH =>
          trans.append("startsWith(");
	case ENDSWITH =>
          trans.append("endsWith(");
	case TEXT =>
          trans.append("text");
          tree.translation = trans;
          return tree.translation;
	case _ =>
          throw new Error("unknown function!");
      }
      var i:int = 0;
      while (i < args.length) {
	if (i > 0) trans.append(", ");
	trans.append(translate(args.apply(i)));
	i = i + 1;
      }
      trans.append(")");
      tree.translation = trans;
      return tree.translation;

    case _ =>
      throw new Error("unknown tree!!!");
  }

  /** Translate several trees.
   */
  private def translate(trees:Array[Tree]):StringBuffer = {
    var trans:StringBuffer = new StringBuffer();
    for (val t <- trees) {
      trans.append(translate(t));
      appendLine(t);
    }
    return trans;
  }

  /** Increment the indentation level.
   */
  override def indent():Translator = {
    level = level + 1;
    return this;
  }

  /** Decrement the indentation level.
   */
  override def undent():Translator = {
    level = level - 1;
    return this;
  }

}
